package com.zxjbyte.yiyi.framework.common.util;

import cn.hutool.core.util.StrUtil;
import com.zxjbyte.yiyi.framework.common.cache.guava.GuavaCacheObjArgs;
import com.zxjbyte.yiyi.framework.common.cache.guava.impl.GuavaCache;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.function.Function;

/**
 * Lambda 字段解析工具类
 *
 * @Author zhangxingjia
 * @Date 2024/12/17 14:34
 * @Version: 1.0
 */
@Slf4j
@UtilityClass
public class LambdaFieldUtil {

    // 解析lambda表达式缓存
    private static final GuavaCache<String, SerializedLambda> CACHE = GuavaCache.newGuavaCache(GuavaCacheObjArgs.builder().maximumSize(200).build());

    /**
     * 从Lambda表达式中解析字段名称
     *
     * @param function Lambda表达式
     * @param <T> 入参类型
     * @param <R> 返回值类型
     * @return 字段名称
     */
    public <T, R> String getFieldName(Function<T, R> function) {
        try {
            SerializedLambda serializedLambda = serialize(function);
            String methodName = serializedLambda.getImplMethodName();
            if (methodName.startsWith("get") || methodName.startsWith("set")) {
                return StrUtil.removePreAndLowerFirst(methodName, 3);
            } else if (methodName.startsWith("is")) {
                return StrUtil.removePreAndLowerFirst(methodName, 2);
            }
            throw new ServiceException("Invalid Lambda method reference：" + methodName);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException("Failed to parse Lambda");
        }
    }

    public SerializedLambda serialize(Function<?, ?> function) {
        return CACHE.get(function.getClass().getName(), () -> {
            Class<?> lambdaClass = function.getClass();
            // 检查是否是 Lambda 表达式（类名包含 "$$Lambda$"）
            if (!lambdaClass.isSynthetic() || !lambdaClass.getName().contains("$$Lambda$")) {
                throw new IllegalArgumentException("Provided object is not a Lambda expression: " + lambdaClass.getName());
            }
            // 检查是否实现了 Serializable
            if (!(function instanceof Serializable)) {
                throw new IllegalArgumentException("Lambda must implement Serializable to support serialization: " + lambdaClass.getName());
            }
            Method writeReplace = function.getClass().getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            return (SerializedLambda) writeReplace.invoke(function);
        });
    }

}
